package com.szeldon.pj.jps.data.store;

import java.io.IOException;

import nu.xom.ParsingException;
import nu.xom.ValidityException;

import com.szeldon.pj.jps.datastore.BooleanObject;
import com.szeldon.pj.jps.datastore.ComplexObject;
import com.szeldon.pj.jps.datastore.IntegerObject;
import com.szeldon.pj.jps.datastore.RealObject;
import com.szeldon.pj.jps.datastore.ReferenceObject;
import com.szeldon.pj.jps.datastore.SBAObject;
import com.szeldon.pj.jps.datastore.SBAObjectId;
import com.szeldon.pj.jps.datastore.StringObject;

public abstract class SBAStore {

    public static final String ROOT_ENTRY_NAME = "entry";

    private Object lock = new Object();

    /**
     * To be invoked before invoking any operations on this store. Uses {@link #abstractInitializeStore()} internally.
     */
    public void initializeStore() {
        abstractInitializeStore();
    }

    /**
     * Every time this method is invoked, it generates a unique {@link SBAObjectId} object being an identifier for
     * object in this store. It uses {@link #abstractGenerateUniqueSBAObjectId()} internally, so each subclass has to
     * define its generation behavior. This method only synchronizes {@link #abstractGenerateUniqueSBAObjectId()}
     * invocation.
     * 
     * @return
     */
    protected synchronized SBAObjectId synchronizedGenerateUniqueSBAObjectId() {
        synchronized (lock) {
            return abstractGenerateUniqueSBAObjectId();
        }
    }

    protected abstract SBAObjectId abstractGenerateUniqueSBAObjectId();

    /**
     * To be implemented by subclasses. It is invoked inside {@link #initializeStore()} method which has to be invoked
     * before operating on {@link SBAStore}.
     */
    public abstract void abstractInitializeStore();

    //
    // CRUD start
    //

    // create
    public abstract StringObject createStringInComplex(String name, String value, ComplexObject complexObject);

    public abstract IntegerObject createIntegerInComplex(String name, int value, ComplexObject complexObject);

    public abstract RealObject createRealInComplex(String name, double value, ComplexObject complexObject);

    public abstract BooleanObject createBooleanInComplex(String name, boolean value, ComplexObject complexObject);

    public abstract ComplexObject createComplexInComplex(String name, ComplexObject complexObject);

    public abstract ReferenceObject createReferenceInComplex(String name, SBAObjectId value, ComplexObject complexObject);

    // retrieve
    public abstract SBAObject retrieve(SBAObjectId sbaObjectId);

    // update
    public abstract boolean updateBooleanValue(SBAObjectId updatedObjectId, boolean newValue);

    public abstract boolean updateComplexObjectByAdding(SBAObjectId updatedObjectId, SBAObjectId addedSubobjectId);

    public abstract boolean updateComplexObjectByRemoval(SBAObjectId updatedObjectId, SBAObjectId removedSubobjectId);

    public abstract boolean updateIntegerValue(SBAObjectId updatedObjectId, int newValue);

    public abstract boolean updateRealValue(SBAObjectId updatedObjectId, double newValue);

    public abstract boolean updateStringValue(SBAObjectId updatedObjectId, String newValue);

    // delete
    public abstract SBAObject delete(SBAObjectId sbaObjectId);

    //
    // CRUD end
    //

    /**
     * @param filePath
     * @throws IOException
     * @throws ParsingException
     * @throws ValidityException
     * @throws IllegalStateException
     */
    public abstract boolean loadObjectsFromXML(String filePath) throws ValidityException, ParsingException,
            IOException, IllegalStateException;

    /**
     * Returns current state of store in standard notation. Each object in new line. <id, name, value>
     * 
     * @return
     */
    public abstract String currentStateToStandardNotation();

    /**
     * 
     * @return
     */
    public abstract ComplexObject retrieveEntryObject();

}
